#pragma once

#include "stdlib.h"
#include <GL/glew.h>

// ===================================================================================
// Global variables & game settings
// ===================================================================================

#define APP_NAME	"Template"
#define WINDOW_W	512
#define WINDOW_H	512

#define RESIZEABLE	1
#define	FULLSCREEN	0
#define REPEATKEYS	0
#define VSYNC		0

struct Config
{
	static bool exitApp;
};

namespace Tmpl {

#define PRIMITIVE_RESTART_ID				0x7FFFFFFF
#define PRIMITIVE_RESTART_ID_SHORT			0xFFFF

inline const GLubyte* BUFFER_OFFSET(size_t bytes)
{
    return reinterpret_cast<const GLubyte *>(0) + bytes;
};

// ===================================================================================
// Inline functions, defines and macro's
// ===================================================================================

#define PI	3.141593f

#define MALLOC64(size, type)		((type*)_aligned_malloc((size) * sizeof(type), 64))
#define MALLOC32(size, type)		((type*)_aligned_malloc((size) * sizeof(type), 32))
#define REALLOC64(ptr, size, type)	((type*)_aligned_realloc(ptr, (size) * sizeof(type), 64))
#define REALLOC32(ptr, size, type)	((type*)_aligned_realloc(ptr, (size) * sizeof(type), 32))
#define FREE64(ptr)					_aligned_free(ptr)
#define FREE32(ptr)					_aligned_free(ptr)

template <typename T> inline void Swap(T& a, T& b)				{T tmp=a; a=b; b=tmp;}

template <typename T> inline T Min(T a, T b)					{return (a < b ? a : b);}
template <typename T> inline T Max(T a, T b)					{return (a > b ? a : b);}
template <typename T> inline T Clamp(T min, T max, T val)		{return (val > max ? max : (val < min ? min : val));}

template <typename T> inline T InvLerp(T start, T end, T val)	{return ((val - start) / (end - start));}
//template <typename T> inline T Lerp(T start, T end, T pct)		{return (start + (end - start) * pct);} //	return (start + (end - start) * pct);
template <typename T, typename R> T Lerp(T start, T end, R pct);
template <typename T> inline T Slerp(T q1, T q2, float t)
{
	float angle = q1.Dot(q2);

	// make sure we use the short rotation
	if (angle < 0.0f)
	{
		q1 *= -1.0f;
		angle *= -1.0f;
	}

	if (angle <= 0.95f) // spherical interpolation
	{
		const float theta = acosf(angle);
		const float invsintheta = 1.0f / sinf(theta);
		const float scale = sinf(theta * (1.0f-t)) * invsintheta;
		const float invscale = sinf(theta * t) * invsintheta;
		return q1*scale + q2*invscale;
	}
	else // linear interploation
	{
		return Lerp(q1, q2, t);
	}
}

template <typename T> inline T MoveTowards(T currentVal, T targetVal, T delta)
{
	if(currentVal + delta < targetVal) return (currentVal + delta);
	if(currentVal - delta > targetVal) return (currentVal - delta);
	return targetVal;
}

inline float Degrees(float rad)		{return rad * (180.0f/PI);}
inline float Radians(float deg)		{return deg * (PI/180.0f);}

inline int Rand(int max)					{return rand() % (max+1);}						// [0-max]
inline int Rand(int min, int max)			{return min + Rand(max-min);}					// [min-max]
inline float RandF(float max)				{return (1.0f/RAND_MAX) * max * (float)rand();}	// [0-max]
inline float RandF(float min, float max)	{return min + RandF(max-min);}					// [min-max]

/* Returns an new allocated char array, copied from the source string */
inline char* DuplicateString(const char* string)
{
	if(!string) return 0;
	unsigned int len = 0;
	while(string[len]) ++len;
	char* dst = new char[len+1];
	for(unsigned int i=0; i<=len; ++i) dst[i] = string[i];
	return dst;
}

extern bool WinFileDialog(char* buffer, int bufferSize, const char* extensions=0);
extern bool WinQuestionDialog(const char* caption, const char* question);
extern void WinMsgDialog(const char* caption, const char* message);
extern void WinSleep(int milliSeconds);
extern double WinGetSystemTime();

// ===================================================================================
// Utility functions and classes
// ===================================================================================

/* Quicksorts an array from index [firstElement] to [lastElement] in ascending order */
template <typename T> void QuickSort(T list[], int firstElement, int lastElement)
{
	int s = firstElement, e = lastElement; 
	if(e - s > 0)
	{
		int pivot = list[firstElement];
		while(e > s)
		{
			while(list[s] <= pivot && s <= lastElement && e > s) s++;
			while(list[e] > pivot && e >= firstElement && e >= s) e--;
			if(e > s) SWAP(list[e], list[s]);
		}
		Swap(list[firstElement], list[e]);
		QuickSort(list, firstElement, e-1);
		QuickSort(list, e+1, lastElement);
	}
}
/* Quicksorts an array from index [firstElement] to [lastElement] in ascending order */
template <typename T, typename K> void QuickSort(T list[], K indices[], int firstElement, int lastElement)
{
	int s = firstElement, e = lastElement; 
	if(e - s > 0)
	{
		int pivot = list[indices[firstElement]];
		while(e > s)
		{
			while(list[indices[s]] <= pivot && s <= lastElement && e > s) s++;
			while(list[indices[e]] > pivot && e >= firstElement && e >= s) e--;
			if(e > s) SWAP(keys[e], keys[s]);
		}
		Swap(list[indices[firstElement]], list[indices[e]]);
		QuickSort(list, indices, firstElement, e-1);
		QuickSort(list, indices, e+1, lastElement);
	}
}

class Timer 
{
public:
	Timer();
	double GetElapsedDp();	/* Returns the elapsed seconds between timing start and stop (double) */
	float GetElapsed();		/* Returns the elapsed seconds between timing start and stop (float) */
	void Start();			/* Set the current time as the start time */
	void Stop();			/* Set the current time as the stop time */
	void Reset();			/* Stops and resets the timer */
private:
	double mStartT, mStopT;
	bool mRunning;
};

}; // namespace Tmpl